/********************************************************************************
 * Project Name		[JavaEE_Web]
 * File Name     	[JSP05.java]
 * Creation Date 	[2015-01-01]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ********************************************************************************/
package _notes.jsp;

/**
 * 自定义标签
 * 
 * @author 不落的太阳(Sean Yang aka ShortPeace)
 * @version 1.0
 * @since jdk 1.8
 * 
 */
public final class JSP05 {

	public void howToCreateTag() {
		/**
		 * 创建并使用自定义标签的步骤<br/>
		 * 
		 * 1. 创建一个标签处理器类, 实现SimpleTag接口/继承SimpleTagSupport类<br/>
		 * 2. 在WEB-INF下新建一个.tld文件[标签库描述文件]<br/>
		 * 3. 在tld文件中添加自定义标签的内容<br/>
		 * 4. 在JSP页面使用taglib指令导入标签<br/>
		 * 5. 使用自定义标签<br/>
		 */
	}

	public void tldFileFormat() {
		/**
		 * 一个示例的tld文件<br/>
		 * 
		 * <code>
		 * <description>TagLib</description>				<!-- 描述TLD文件 -->
		 * <display-name>TagLib Core</display-name>
		 * <tlib-version>1.0</tlib-version>
		 *
		 * <short-name>s</short-name						<!-- JSP页面taglib标签的prefix属性引用 -->
		 * <uri>http://www.sean.org/taglib/core</uri>		<!-- tld文件的ID, 用来唯一标识当前tld文件, 多个tld文件的URI不能重复, 通过JSP页面taglib标签的uri属性引用 -->
		 *
		 * <tag>
		 *     <name>simpleTag</name>						<!-- 标签名称 -->
		 *     <tag-class>tag.SimpleTag</tag-class>			<!-- 标签处理器的全类名 -->
		 *     <body-content>empty</body-content>			<!-- 指定标签体类型 -->
		 *     <attribute>									<!-- 指定属性 -->
		 *         <name>attribute1</name>					<!-- 属性名, 需和标签处理器类的setter方法定义的属性相同 -->
		 *         <required>true</required>				<!-- 是否为一定要有该属性 -->
		 *         <rtexprvalue>true</rtexprvalue>			<!-- run time expression value, 是否可以使用EL表达式动态传值-->
		 *     </attribute>
		 * </tag>
		 * </code><br/>
		 * 
		 * <body-content>有三种类型<br/>
		 * 1. empty: 没有标签体<br/>
		 * 2. scriptless: 标签体可以包含EL表达式和JSP动作元素, 但不能包含JSP的脚本元素<br/>
		 * 3. tagdependent: 表示标签体交由标签本身去解析处理, 若指定tagdependent,
		 * 在标签体中的所有代码都会原封不动的交给标签处理器, 而不是将执行结果传递给标签处理器<br/>
		 * 
		 * 标签的属性值只能是基本数据类型和String, JSP引擎会将标签内的属性字符串自动转换成相应的类型<br/>
		 * 如果标签是其他类型, 比如Date类型<br/>
		 * <code>
		 * // 标签内定义属性
		 * private Date date; 
		 * 
		 * // JSP页面
		 * <%
		 *     pageContext.setAttribute("date", new Date());
		 * %>
		 * // 方式1
		 * <s:dateTag date="${date}" />
		 * // 方式2
		 * <s:dateTag date="<%=new Date()%>" />
		 * </code><br/>
		 */
	}

	public void simpleTag() {
		/**
		 * SimpleTag接口<br/>
		 * 
		 * setJspContext(): 该方法把代表JSP页面的pageContext对象传递给标签处理器对象<br/>
		 * setParent(): 该方法把父标签处理器对象传递给当前标签处理器对象<br/>
		 * getParent(): 该方法用于获得标签的父标签处理器对象<br/>
		 * setJspBody(): 该方法用于把代表标签体的 JspFragment对象传递给标签处理器对象<br/>
		 * doTag(): 该方法用于完成所有的标签逻辑, 该方法可以抛出 javax.servlet.jsp.SkipPageException,
		 * 用于通知Web容器不再执行JSP页面中位于结束标记后面的内容<br/>
		 */

		/**
		 * 实现SimpleTag接口的标签处理器类的生命周期<br/>
		 * 
		 * 1. setJSPContext() JSP引擎将代表JSP页面的pageContext对象传递给标签处理器对象<br/>
		 * 2. setParent() JSP引擎将父标签处理器对象传递给当前标签处理器对象, 只有存在父标签时, JSP引擎才会调用该方法
		 * <br/>
		 * 3. setXXX() 设置标签属性, 只有定义属性才调用该方法<br/>
		 * 4. setJspBody() 若存在标签体, JSP引擎将把标签体封装成一个JspFragment对象,
		 * 调用setJspBody将JspFragment对象传递给标签处理器对象, 若标签体为空则setJspBody不会被JSP引擎调用
		 * <br/>
		 * 5. doTag() 容器调用标签处理器对象的doTag方法执行标签逻辑<br/>
		 */
	}

	public void simpleTagSupport() {
		/**
		 * SimpleTagSupport类<br/>
		 * 
		 * SimpleTagSupport是SimpleTag接口的一个实现类<br/>
		 * SimpleTagSupport实现了SimpleTag接口中的方法,
		 * 内部以成员变量的形式保存了setJspContext()和setJspBody()传递进来的参数<br/>
		 * 定义了如下两个方法来返回这两个参数<br/>
		 * 1. getJspContext(): 该方法用于返回代表调用页面的JspContext对象<br/>
		 * 2. getJspBody(): 该方法用于得到代表标签体的JspFragment对象<br/>
		 */
	}

	public void jspFragment() {
		/**
		 * JspFragment类: 代表了标签体的内容<br/>
		 * 该类的实例对象代表了JSP页面中的一段符合JSP语法规范的JSP片段, 该片段不能包含 JSP脚本元素[<%...%>], <br/>
		 * 
		 * <code>
		 * <s:upperCase>hello java world</s:upperCase>
		 * </code><br/>
		 * 
		 * JSP引擎在处理简单标签的标签体时, 会把标签体内容用一个JspFragment对象表示,
		 * 并调用标签处理器对象的setJspBody()把JspFragment对象传递给标签处理器对象,
		 * 得到代表标签体的JspFragment对象后, 标签开发者就可以在标签处理器中根据需要调用JspFragment对象的方法,
		 * 进而决定如何处理标签体<br/>
		 * 
		 * 在SimpleTagSupport类中定义了一个getJspBody()的方法, 用于获得JspFragment对象<br/>
		 * 
		 * 主要方法<br/>
		 * getJspContext(): 用于返回代表调用页面的JspContext对象<br/>
		 * 
		 * invoke(java.io.Writer out):<br/>
		 * 用于执行JspFragment对象所代表的JSP代码片段, 在 doTag()中可以根据需要调用该方法<br/>
		 * invoke()方法可以控制是否执行和输出标签体的内容, 是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出<br/>
		 * 1. 在标签处理器中没有调用JspFragment.invoke方法, 则忽略标签体内容<br/>
		 * 2. 在标签处理器中重复调用JspFragment.invoke方法, 则标签体内容会被重复执行<br/>
		 * 3. 若想在标签处理器中修改标签体内容, 只需在调用invoke方法时指定一个可取出结果数据的输出流对象[如StringWriter],
		 * 让标签体的执行结果输出到该输出流对象中, 然后从该输出流对象中取出数据进行修改后再输出到目标页面, 即可达到修改标签体的目的<br/>
		 * 
		 * 
		 * 参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中<br/>
		 * out = null, 等同于invoke(getJspContext().getOut), 即直接把标签体内容输出到页面<br/>
		 * out = new StringWriter(), 在调用invoke()方法时指定了一个可取得输出结果数据的输出流对象<br/>
		 */
	}

	public void parentChildTag() {
		/**
		 * 父子标签<br/>
		 * 1. 父标签无法获取子标签的引用, 父标签仅把子标签作为标签体来使用<br/>
		 * 2. 子标签可以通过 getParent()来获取父标签的引用, 若子标签有父标签,
		 * JSP引擎会把代表父标签的引用通过setParent(JspTag parent)赋给标签处理器<br/>
		 * 3. 子标签需要继承SimpleTagSupport或实现SimpleTag接口的getParent()方法<br/>
		 * 4. 父标签的类型是JspTag类型, 该接口是一个空接口, 用来统一SimpleTag和 Tag的, 实际使用需要进行类型的强制转换
		 * <br/>
		 * 5. 在tld文件中, 无需为父标签有额外的配置, 但子标签是是以标签体的形式存在的, 所以父标签的<body-content>需要为
		 * scriptless<br/>
		 * 
		 * 参见jsp.tag.cases.ChooseTag, jsp.tag.cases.WhenTag,
		 * jsp.tag.cases.OtherwiseTag 示例<br/>
		 */
	}

	public void packToJar() {
		/**
		 * 把自己写的tag打包成taglib<br/>
		 * 
		 * 1. 新建一个Java项目<br/>
		 * 2. 将taglib源码拷贝到第一步新建的项目<br/>
		 * 3. 新建一个lib文件夹, 将jsp-api.jar和servlet-api.jar放入<br/>
		 * 4. 将以上两个jar加入到build path[右键Build Path]<br/>
		 * 5. 新建一个META-INF文件夹, 将tld文件放入其中<br/>
		 * 6. 点右键Export-JAR file[.classpath, .project不勾选]<br/>
		 */
	}
}
